package com.droidblackbox.manager;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import android.app.Activity;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.droidblackbox.R;
import com.droidblackbox.info.SystemInfo;

public final class FileManager {

	private static FileManager instance = null;

	public static final String FOLDER_RECORD;
	public static final String FOLDER_BLACK_BOX;
	public static final String FILE_README;
	public static final String FILE_LOG;
	public static final String DATA_EXTENSION;
	public static final String TEXT_EXTENSION;
	public static final String MEDIA_EXTENSION;
	public static final String TEMP;
	public static final String SELECT_FILE;

	static {
		FOLDER_RECORD = FolderName.RecordMedia.toString();
		FOLDER_BLACK_BOX = FolderName.RecordDocument.toString();
		FILE_README = FileName.ReadMe.toString();
		FILE_LOG = FileName.Log.toString();
		DATA_EXTENSION = ".bbi"; // BlackBoxInfo
		TEXT_EXTENSION = ".txt";
		MEDIA_EXTENSION = ".mp4";
		TEMP = "temp";
		SELECT_FILE = "select";
	}

	private Map<FolderName, File> folderManager = null;
	private Map<FileName, File> fileManager = null;

	private File dir = null;

	private String root;
	private String appName;
	private String warning;

	private enum FolderName {
		RecordMedia, RecordDocument
	};

	private enum FileName {
		ReadMe, Log
	};

	public synchronized static FileManager setInstance(Activity activity) {
		if (instance == null) {
			synchronized (FileManager.class) {
				if (instance == null) {
					instance = new FileManager(activity);
				}
			}
		}

		return getInstance();
	}

	private synchronized static FileManager getInstance() {
		if (instance == null)
			return null;
		return instance;
	}

	private FileManager() {
	}

	private FileManager(Activity activity) {
		appName = activity.getResources().getString(R.string.app_name);
		warning = activity.getResources().getString(R.string.warning_readme);
	}

	public void onInit() throws IOException {
		onCreate();
		onReadMe();

	}

	public void onCreate() {
		create();
	}

	public void onReadMe() throws IOException {
		setReadMe();
	}

	public static File getFile(String name) throws IOException {
		return getInstance().getFile(name, 0);
	}

	public static boolean delete(String name) throws NullPointerException,
			IOException {
		String string = name + MEDIA_EXTENSION;
		return getInstance().delete(string, 0);
	}

	public static boolean delete(File file) throws NullPointerException,
			IOException {
		return getInstance().delete(file.getName(), 0);
	}

	private void create() {
		dir = Environment.getExternalStorageDirectory();
		root = dir.getPath() + "/" + appName + "/";

		folderManager = new HashMap<FolderName, File>();
		fileManager = new HashMap<FileName, File>();

		folderManager.put(FolderName.RecordMedia, new File(root,
				FolderName.RecordMedia.toString()));
		folderManager.put(FolderName.RecordDocument, new File(root,
				FolderName.RecordDocument.toString()));

		fileManager.put(FileName.ReadMe,
				new File(root, FileName.ReadMe.toString() + TEXT_EXTENSION));
		fileManager.put(FileName.Log, new File(root, FileName.Log.toString()
				+ TEXT_EXTENSION));

		fileExists();
		folderExists();

	}

	private void folderExists() {
		FolderName[] folder = FolderName.values();
		for (FolderName f : folder) {
			if (!folderManager.get(f).exists())
				folderManager.get(f).mkdirs();
		}
	}

	private void fileExists() {
		FileName[] file = FileName.values();

		for (FileName f : file) {
			if (!fileManager.get(f).exists())
				fileManager.put(f,
						new File(root, f.toString() + TEXT_EXTENSION));
		}
	}

	private void setReadMe() throws IOException {
		File f = fileManager.get(FileName.ReadMe);
		if (!f.exists()) {
			FileWriter fw = new FileWriter(f, true);
			BufferedWriter bw = new BufferedWriter(fw);
			StringBuffer sb = new StringBuffer();

			sb.append(warning).append("\r\n");

			bw.write(sb.toString());
			bw.flush();
			bw.close();
			fw.close();
		}
	}

	private File getFile(String name, int id) throws IOException {
		File f = null;
		FileName[] files = FileName.values();

		for (FileName fn : files) {
			if (fn.toString().equals(name)) {
				f = fileManager.get(fn);
				break;
			}
		}

		if (f == null)
			f = getFolder(name);
		return f;

	}

	private File getFolder(String name) throws IOException {
		File f = null;
		FolderName[] files = FolderName.values();

		for (FolderName fn : files) {
			if (fn.toString().equals(name)) {
				f = folderManager.get(fn);
				break;
			}

		}
		return f;
	}

	public static File[] getFileFromFolder(String folderName)
			throws IOException, NullPointerException {
		File file = null;
		if (folderName.equals(FileManager.FOLDER_RECORD))
			file = FileManager.getFile(FileManager.FOLDER_RECORD);
		else if (folderName.equals(FileManager.FOLDER_BLACK_BOX))
			file = FileManager.getFile(FileManager.FOLDER_BLACK_BOX);
		else
			file = null;

		File[] files = file.listFiles();
		return files;
	}

	public static File getLastModifiedFile() throws NullPointerException,
			IOException {
		File[] files = FileManager.getFileFromFolder(FileManager.FOLDER_RECORD);
		Stack<File> stack = new Stack<File>();
		for (File file : files) {
			Calendar calendar = Calendar.getInstance();
			calendar.setTimeInMillis(file.lastModified());

			StringBuffer sb = new StringBuffer();
			sb.append(calendar.get(Calendar.YEAR));
			sb.append(calendar.get(Calendar.MONTH) + 1);
			sb.append(calendar.get(Calendar.DAY_OF_MONTH));
			sb.append(calendar.get(Calendar.HOUR_OF_DAY));
			sb.append(calendar.get(Calendar.MINUTE));
			sb.append(calendar.get(Calendar.SECOND));

		}

		return stack.pop();
	}

	private boolean delete(String name, int id) throws NullPointerException,
			IOException {
		boolean isRecordFile = false;
		boolean isBlackBoxFile = false;

		File[] recordFiles = getFileFromFolder(FOLDER_RECORD);
		for (File file : recordFiles) {
			if (file.getName().equals(name)) {
				file.delete();
				isRecordFile = true;
				break;
			}
		}

		File[] blackboxFiles = getFileFromFolder(FOLDER_BLACK_BOX);
		String blackboxName = name.replaceAll(MEDIA_EXTENSION, DATA_EXTENSION);
		for (File file : blackboxFiles) {
			if (file.getName().equals(blackboxName)) {
				file.delete();
				isBlackBoxFile = true;
				break;
			}
		}

		if (isRecordFile == true && isBlackBoxFile == true)
			return true;
		else
			return false;
	}

	public static boolean renameTo(File fromFile, File toFile)
			throws IOException {
		return getInstance().rename(fromFile, toFile);
	}

	private boolean rename(File fromFile, File toFile) throws IOException {

		if (fromFile.isDirectory()) {
			File[] files = fromFile.listFiles();
			if (files == null) {
				return fromFile.renameTo(toFile);
			} else {
				if (!toFile.mkdirs()) {
					return false;
				}
				for (File eachFile : files) {
					File toFileChild = new File(toFile, eachFile.getName());
					if (eachFile.isDirectory()) {
						if (!rename(eachFile, toFileChild)) {
							return false;
						}
					} else {
						if (!eachFile.renameTo(toFileChild)) {
							return false;
						}
					}
				}
				return fromFile.delete();
			}
		} else {
			return fromFile.renameTo(toFile);
		}
	}

	public static List<Object> readerListObject(File file) {
		return getInstance().readListObject(file);
	}

	@SuppressWarnings("unchecked")
	private List<Object> readListObject(File file) {
		FileInputStream fis = null;
		ObjectInputStream ois = null;
		List<Object> list = null;
		try {
			fis = new FileInputStream(file);
			ois = new ObjectInputStream(fis);

			list = (List<Object>) ois.readObject();
		} catch (ClassNotFoundException e) {
			Log.e("DroidBlackBox", file.getName() + "Class Reader Error");
		} catch (IOException e) {
			Log.e("DroidBlackBox", file.getName() + "Reader Error");
		} finally {
			try {
				ois.close();
				fis.close();
			} catch (NullPointerException npe) {
			} catch (IOException ioe) {
			}
		}
		return list;
	}

	public static void writeLog(StringBuffer msg) {
		getInstance().write(msg);
	}

	public static void writeLog(String msg) {
		getInstance().write(msg);
	}

	public static void writeListObject(File file, List<Object> list) {
		getInstance().write(file, list);
	}

	public static void writeFile(File file, StringBuffer msg) {
		getInstance().write(file, msg);
	}

	public static void writeFile(File file, String msg) {
		getInstance().write(file, msg);
	}

	private void write(File file, List<Object> list) {
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;
		try {
			fos = new FileOutputStream(file);
			oos = new ObjectOutputStream(fos);

			oos.writeObject(list);
			oos.reset();

		} catch (IOException e) {
			Log.e("DroidBlackBox", file.getName() + "Write Error");
		} finally {
			try {
				oos.close();
				fos.close();
			} catch (IOException ioe) {
			}
		}

	}

	private void write(File file, StringBuffer msg) {
		FileWriter fw = null;
		BufferedWriter bw = null;
		try {
			fw = new FileWriter(file, true);
			bw = new BufferedWriter(fw);

			bw.write(msg.toString());
			bw.flush();

		} catch (IOException e) {
			Log.e("DroidBlackBox", file.getName() + "Write Error");
		} finally {
			try {
				bw.close();
				fw.close();
			} catch (IOException ioe) {
			}
		}
	}

	private void write(File file, String msg) {
		FileWriter fw = null;
		BufferedWriter bw = null;
		try {
			fw = new FileWriter(file, true);
			bw = new BufferedWriter(fw);

			bw.write(msg);
			bw.flush();

		} catch (IOException e) {
			Log.e("DroidBlackBox", file.getName() + "Write Error");
		} finally {
			try {
				bw.close();
				fw.close();
			} catch (IOException ioe) {
			}
		}
	}

	private void write(StringBuffer msg) {
		FileWriter fw = null;
		BufferedWriter bw = null;
		try {
			File file = FileManager.getFile(FileManager.FILE_LOG);
			fw = new FileWriter(file, true);
			bw = new BufferedWriter(fw);

			bw.write(msg.toString());
			bw.flush();

		} catch (IOException e) {
			Log.e("DroidBlackBox", "Log File Error");
		} finally {
			try {
				bw.close();
				fw.close();
			} catch (IOException ioe) {
			}
		}
	}

	private void write(String msg) {
		FileWriter fw = null;
		BufferedWriter bw = null;
		try {
			File file = FileManager.getFile(FileManager.FILE_LOG);
			fw = new FileWriter(file, true);
			bw = new BufferedWriter(fw);

			bw.write(msg);
			bw.flush();

		} catch (IOException e) {
			Log.e("DroidBlackBox", "Log File Error");
		} finally {
			try {
				bw.close();
				fw.close();
			} catch (IOException ioe) {
			}
		}
	}

	public static SystemInfo getSystemInfo(Context context) {
		return getInstance().readInternalStorage(context, 0);
	}

	private SystemInfo readInternalStorage(Context context, int index) {
		final String FILENAME = context.getResources().getString(
				R.string.app_name);
		SystemInfo info = null;
		try {
			FileInputStream fis = context.openFileInput(FILENAME);
			ObjectInputStream ois = new ObjectInputStream(fis);

			info = (SystemInfo) ois.readObject();

			ois.close();
			fis.close();

		} catch (FileNotFoundException e) {
			createInternalStorage(context);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return info;
	}

	private void createInternalStorage(Context context) {
		try {

			final String FILENAME = context.getResources().getString(
					R.string.app_name);

			FileOutputStream fos = context.openFileOutput(FILENAME,
					Context.MODE_PRIVATE);
			ObjectOutputStream oos = new ObjectOutputStream(fos);

			SystemInfo info = new SystemInfo();

			oos.writeObject(info);

			oos.flush();
			oos.close();
			fos.close();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			readInternalStorage(context, 0);
		}

	}

	public static void setSystemInfo(Context context, SystemInfo systemInfo) {
		getInstance().writeInternalStorage(context, systemInfo, 0);
	}

	private void writeInternalStorage(Context context, SystemInfo systemInfo,
			int index) {
		final String FILENAME = context.getResources().getString(
				R.string.app_name);
		try {
			FileOutputStream fos = context.openFileOutput(FILENAME,
					Context.MODE_PRIVATE);
			ObjectOutputStream oos = new ObjectOutputStream(fos);

			oos.writeObject(systemInfo);

			oos.flush();
			oos.close();
			fos.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}
